package middleware.p2p.replication;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import middleware.p2p.log.VectorClock;
import middleware.p2p.transaction.TransactionResponse;
import middleware.p2p.transaction.log.TransactionLoggerLocal;

import org.apache.log4j.Logger;

/**
 * Session Bean implementation class ReplicaManager
 */
@Stateless
@WebService(name="ReplicaManager", serviceName="ReplicaManagerService")
public class ReplicaManager implements ReplicaManagerRemote {
	@EJB(name="ejb/TransactionLogger",
			beanInterface=middleware.p2p.transaction.log.TransactionLoggerLocal.class)
	private TransactionLoggerLocal transLogger;
	@EJB(name="ejb/ReplicaCache",
			beanInterface=middleware.p2p.replication.ReplicaTransactionCacheLocal.class)
	private ReplicaTransactionCacheLocal replicaCache;
	@EJB(name="ejb/ReplicaQueue",
			beanInterface=middleware.p2p.replication.ReplicaRequestQueueLocal.class)
	private ReplicaRequestQueueLocal queue;	
	private Logger logger=Logger.getLogger(this.getClass());
    /**
     * Default constructor. 
     */
    public ReplicaManager() {
    	super();
        try {
	        Context ctx = new InitialContext();
			this.transLogger = (TransactionLoggerLocal)ctx.lookup("java:comp/env/ejb/TransactionLogger");
			this.replicaCache = (ReplicaTransactionCacheLocal)ctx.lookup("java:comp/env/ejb/ReplicaCache");
			this.queue = (ReplicaRequestQueueLocal)ctx.lookup("java:comp/env/ejb/ReplicaQueue");
		} catch (NamingException e) {
			logger.error(e);
		}
    	
    }
	@Override
	public boolean canCommit(String transId) {
		boolean response=false;
		ReplicaCacheEntry entry=this.replicaCache.getReplicaEntry(transId);
		if (entry!=null) {
			switch (entry.getAction()) {
				case Store:
					try {
						File tempFolder=new File(".."+File.separator+"logs"+File.separator+"replicaStore"+File.separator+"temp");
						tempFolder.mkdirs();
						File replicaTemp=File.createTempFile("rpl", null, tempFolder);
						PrintStream outFile=new PrintStream(replicaTemp);
						outFile.print(entry.getContent());
						outFile.close();
						response=this.replicaCache.registerTempFile(transId, replicaTemp);
					} catch (IOException ioe) {
						logger.error(ioe);
						response=false;
					}
					break;
				case Get:
					response=Boolean.parseBoolean(entry.getContent());
					break;
			}			
		}
		logger.debug("canCommit "+transId+" => Response: "+(response?"READY":"NOT_READY"));
		this.transLogger.writePartecipantVote(transId, response);
		return response;
	}
	@Override
	public boolean doAbort(String transId) {
		logger.debug("doAbort "+transId);
		this.transLogger.writeResponse(transId, TransactionResponse.Abort);
		ReplicaCacheEntry entry=this.replicaCache.getReplicaEntry(transId);
		if (entry!=null) {
			switch (entry.getAction()) {
				case Store:
					this.replicaCache.deleteReplicaEntry(transId);					
					break;
				case Get:
					this.replicaCache.deleteReplicaEntry(transId);
					break;
			}			
		}
		logger.debug("Abort Ack Sent");
		this.transLogger.writeDone(transId);
		return true;
	}

	@Override
	public boolean doCommit(String transId) {
		logger.debug("doCommit "+transId);
		ReplicaCacheEntry entry=this.replicaCache.getReplicaEntry(transId);
		if (entry!=null) {
			switch (entry.getAction()) {
				case Store:
					try {
						BufferedReader inFile=new BufferedReader(new FileReader(entry.getTempFile()));
						File rplFile=new File(".."+File.separator+"logs"+File.separator+"replicaStore"+File.separator+entry.getComponentId()+".rpl");						
						rplFile.getParentFile().mkdirs();
						//System.out.println(rplFile.getAbsolutePath());
						PrintStream outFile=new PrintStream(rplFile);
						String tLine;
						while ((tLine=inFile.readLine())!=null) {
							outFile.println(tLine);
						}
						inFile.close();
						outFile.close();
						this.replicaCache.deleteReplicaEntry(transId);
					} catch (FileNotFoundException fnfe) {
						logger.error(fnfe);
					} catch (IOException ioe) {
						logger.error(ioe);
					}
					break;				
				case Get:
					this.replicaCache.deleteReplicaEntry(transId);
					break;
			}			
		}
		this.transLogger.writeResponse(transId, TransactionResponse.Commit);
		logger.debug("Commit Ack Sent");
		this.transLogger.writeDone(transId);
		return true;
	}

	@Override
	public String getReplica(String componentId, String transId, String coordWsdl,VectorClock vc,String responseAddr) {
		ReplicaRequest event=new ReplicaRequest();
		event.setComponentId(componentId);
		event.setVclock(vc);
		event.setGet(true);
		event.setPush(false);
		event.setTransId(transId);
		event.setCoordWsdl(coordWsdl);
		event.setResponseAddr(responseAddr);
		this.queue.enqueue(event);
		return "";
	}

	@Override
	public boolean storeReplica(String componentId, String replica,
			String transId, String coordWsdl,VectorClock vc,String responseAddr) {
		ReplicaRequest event=new ReplicaRequest();
		event.setComponentId(componentId);
		event.setVclock(vc);
		event.setReplica(replica);
		event.setGet(false);
		event.setPush(true);
		event.setTransId(transId);
		event.setCoordWsdl(coordWsdl);
		event.setResponseAddr(responseAddr);
		this.queue.enqueue(event);
		return true;
	}
	@Override
	public boolean resetClock(String cid) {
		this.queue.resetClock(cid);
		return true;
	}
}